package com.doge.walknovel.business.reader.comments

import androidx.lifecycle.MutableLiveData
import androidx.paging.DataSource
import com.doge.walknovel.api.*
import com.doge.walknovel.api.model.CommentInfo
import com.doge.walknovel.core.base.LoadStatus
import com.doge.walknovel.core.base.adapters.*
import com.doge.walknovel.utils.isMainThread
import com.doge.walknovel.utils.runOnUI
import kotlin.math.max

class ReaderCommentsRepository(
    private val novelId: Int,
    private val chapterId: Int
) {
    val loading = MutableLiveData<LoadStatus>()
    private var state: ListState<CommentInfo> = ListState()
    private var dataSource: XPagedKeyDataSource? = null

    fun requestChapterComments(initial: Boolean) {
        require(isMainThread())
        if (initial) loading.value = LoadStatus.LOADING
        val nextPage = if (initial) PAGE_INITIAL else (state.page + 1)
        api
            .novelCommentList(novelId, nextPage)
            .map(ApiResultMapTransformer())
            .subscribeOnIO()
            .observeOnWork()
            .subscribe(object : ObserverAdapter<List<CommentInfo>>() {
                override fun onNext(t: List<CommentInfo>) {
                    reduceOnNext(initial, t)
                }

                override fun onError(e: Throwable) {
                    reduceOnError()
                }
            })
    }

    private fun reduceOnNext(initial: Boolean, data: List<CommentInfo>) {
        state = state.reduceOnNext(initial, data) {
            it.commentId.toString()
        }
        dataSource?.invalidate()
        runOnUI { loading.value = LoadStatus.SUCCESS }
    }

    private fun reduceOnError() {
        state = state.copy(
            list = state.list.remove(ID_MORE_LOADING).add(ID_NO_MORE)
        )
        dataSource?.invalidate()
        runOnUI { loading.value = LoadStatus.FAILURE }
    }

    fun requestSubmitChapterComments(content: String?) {
        require(isMainThread())
        if (content == null) return
        api.novelSubmitComment(
            novelId, content, null, 1
        )
            .map(ApiResultMapTransformer())
            .subscribeOnIO()
            .observeOnWork()
            .subscribe(object : ObserverAdapter<CommentInfo>() {
                override fun onNext(t: CommentInfo) {
                    reduceNewComments(t)
                }
            })
    }

    private fun reduceNewComments(data: CommentInfo) {
        state = state.copy(
            list = state.list.insertOrReplace(data.commentId.toString(), 0),
            data = state.data.appendData(listOf(data)) { data.commentId.toString() }
        )
        dataSource?.invalidate()
    }

    fun requestSubmitReplyComments(commentId: Int, content: String?, score: Int?) {
        require(isMainThread())
        if (content == null) return
        api.novelSubmitComment(
            commentId, content, score, 3
        )
            .map(ApiResultMapTransformer())
            .subscribeOnIO()
            .observeOnWork()
            .subscribe(object : ObserverAdapter<CommentInfo>() {
                override fun onNext(t: CommentInfo) {
                    val data = state.data[commentId.toString()] ?: return
                    reduceUpdateComment(data.copy(replyCount = max(0, (data.replyCount ?: 0) + 1)))
                }
            })
    }

    fun requestLikeComments(data: CommentInfo) {
        api
            .novelLikeComment(data.commentId)
            .compose(ApiResultComposeTransformer())
            .subscribeOnIO()
            .observeOnWork()
            .subscribe(object : ObserverAdapter<Any?>() {
                override fun onNext(t: Any?) {
                    reduceUpdateComment(
                        data.copy(
                            likeFlag = 1,
                            likeCount = max(0, (data.likeCount ?: 0) + 1)
                        )
                    )
                }
            })
    }

    fun requestUnLikeComments(data: CommentInfo) {
        api
            .novelUnLikeComment(data.commentId)
            .compose(ApiResultComposeTransformer())
            .subscribeOnIO()
            .observeOnWork()
            .subscribe(object : ObserverAdapter<Any?>() {
                override fun onNext(t: Any?) {
                    reduceUpdateComment(
                        data.copy(
                            likeFlag = 0,
                            likeCount = max(0, (data.likeCount ?: 0) - 1)
                        )
                    )
                }
            })
    }

    private fun reduceUpdateComment(data: CommentInfo) {
        state = state.copy(
            data = state.data.replace {
                var result: CommentInfo? = null
                if (data.commentId == it.commentId) result = data
                result
            }
        )
        dataSource?.invalidate()
    }

    fun dataSource(): DataSource.Factory<Int, Cell> =
        object : DataSource.Factory<Int, Cell>() {
            override fun create(): DataSource<Int, Cell> {
                return object : XPagedKeyDataSource() {
                    override fun totalCount(): Int {
                        return state.list.size
                    }

                    override fun loadRange(start: Int, count: Int): List<Cell?> {
                        return state.list.mapToCell(start, count) {
                            Cell(ItemType.Comment, it, extra = state.data[it])
                        }
                    }
                }.also { dataSource = it }
            }
        }
}